`include "common.svh"
module store_buffer #(
    parameter SIZE = 16
) (
    input clk,
    input rst,
    output empty,
    input i_valid,
    // input ISSUE_OP i_iop,
    input MEM_REQ i_req,
    output i_ready,

    output o_violation_detected,
    input i_violation_valid,
    input MEM_REQ i_violation_req  /* verilator public */,

    input i_rob_order_valid,
    input ROB_PTR i_rob_order_rob_idx,

    // output o_rob_finish_valid,
    // output ROB_PTR o_rob_finish_rob_idx,

    output o_dmem_valid,
    output MEM_REQ o_dmem_req,
    input o_dmem_ready

);
  localparam AW = $clog2(SIZE);
  localparam MEM_REQ_SIZE = $size(MEM_REQ);
  //   localparam ISSUE_OP_SIZE = $size(ISSUE_OP);

  wire i_fire = i_ready & i_valid;
  wire o_dmem_fire = o_dmem_ready & o_dmem_valid;
  logic o_valid, o_ready;

  wire o_fire = o_valid & o_ready;

  //   ISSUE_OP [SIZE-1:0] store_iops;
  MEM_REQ [SIZE-1:0] store_reqs;
  logic [SIZE-1:0] store_mmio;
  logic [SIZE-1:0] store_entry_valid;
  logic [SIZE-1:0] store_entry_valid_next;

  logic [SIZE-1:0] store_entry_committed;
  logic [SIZE-1:0] store_entry_committed_set;
  logic [SIZE-1:0] store_entry_committed_clr;

  wire handle_mmio = |(store_mmio & store_entry_valid);

  logic [AW:0] wptr, rptr;
  wire [AW:0] rptr_next = rptr + 'b1;
  wire [AW:0] wptr_next = wptr + 'b1;
  //   wire [AW:0] s0_rptr = rptr;
  wire [AW-1:0] s0_raddr = rptr[AW-1:0];
  wire [AW-1:0] waddr = wptr[AW-1:0];
  wire full = (wptr[AW-1:0] == rptr[AW-1:0]) & (wptr[AW] != rptr[AW]);
  assign empty   = wptr == rptr;
  assign i_ready = ~(full | handle_mmio);

  reg_lr #(AW + 1) wptr_r (
      .load(i_fire),
      .din (wptr_next),
      .dout(wptr),
      .*
  );

  reg_lr #(AW + 1) rptr_r (
      .load(o_fire),
      .din (rptr_next),
      .dout(rptr),
      .*
  );

  //   ISSUE_OP s0_store_iop = store_iops[s0_raddr];
  MEM_REQ s0_store_req = store_reqs[s0_raddr];
  wire s0_store_entry_valid = store_entry_valid[s0_raddr];
  wire s0_store_entry_committed = store_entry_committed[s0_raddr] | store_entry_committed_set[s0_raddr];

  assign o_valid = s0_store_entry_valid & s0_store_entry_committed;
  assign o_ready = o_dmem_ready | (~o_dmem_valid);
  //DMEM REQ
  wire o_dmem_valid_set = o_fire;  //s0_store_entry_valid & s0_store_entry_committed;
  wire o_dmem_valid_clr = o_dmem_fire;
  reg_lr #(1) o_dmem_valid_r (
      .load(o_dmem_valid_set | o_dmem_valid_clr),
      .din (o_dmem_valid_set),
      .dout(o_dmem_valid),
      .*
  );

  reg_l #(MEM_REQ_SIZE) o_dmem_req_r (
      .load(o_dmem_valid_set),
      .din (s0_store_req),
      .dout(o_dmem_req),
      .*
  );
  //   ISSUE_OP o_dmem_iop;
  //   reg_l #(ISSUE_OP_SIZE) o_dmem_iop_r (
  //       .load(o_dmem_valid_set),
  //       .din (s0_store_iop),
  //       .dout(o_dmem_iop),
  //       .*
  //   );

//   assign o_rob_finish_valid   = o_dmem_fire;
//   assign o_rob_finish_rob_idx = o_dmem_req.rob_idx;


  genvar gi;
  generate
    //Generate violation check result
    logic [SIZE-1:0] violation_detected;
    for (gi = 0; gi < SIZE; gi = gi + 1) begin : DetectViolation
      assign violation_detected[gi] = store_entry_valid[gi] & store_reqs[gi].addr[`WORD_BITS-1:3] == i_violation_req.addr[`WORD_BITS-1:3];
    end
    reg_r #(1) o_violation_detected_r (
        .din (i_violation_valid & (|violation_detected)),
        .dout(o_violation_detected),
        .*
    );

    //Generate store buffer entry signal
    logic [SIZE-1:0] we;
    // ISSUE_OP [SIZE-1:0] w_iop;
    MEM_REQ [SIZE-1:0] w_req;
    for (gi = 0; gi < SIZE; gi = gi + 1) begin : StoreBufferSignal
      assign we[gi] = i_fire & (waddr == gi);
      //   assign w_iop[gi] = i_iop;
      assign w_req[gi] = i_req;
      //   reg_l #(ISSUE_OP_SIZE) store_iops_r (
      //       .load(we[gi]),
      //       .din (w_iop[gi]),
      //       .dout(store_iops[gi]),
      //       .*
      //   );

      reg_l #(MEM_REQ_SIZE) mem_reqs_r (
          .load(we[gi]),
          .din (w_req[gi]),
          .dout(store_reqs[gi]),
          .*
      );
      assign store_mmio[gi] = store_reqs[gi].mmio;

      wire after_wptr = gi < wptr[AW-1:0];
      wire before_rptr = gi >= rptr[AW-1:0];
      assign store_entry_valid_next[gi] = (wptr[AW] != rptr[AW]) ? (after_wptr | before_rptr) : (after_wptr & before_rptr);
      assign store_entry_valid[gi] = store_entry_valid_next[gi];
      //   reg_r #(1) store_entry_valid_r (
      //       .din (store_entry_valid_next[gi]),
      //       .dout(store_entry_valid[gi]),
      //       .*
      //   );

      assign store_entry_committed_clr[gi] = we[gi];
      assign store_entry_committed_set[gi] = store_entry_valid[gi] & (i_rob_order_valid & (i_rob_order_rob_idx == store_reqs[gi].rob_idx));
      reg_lr #(1) store_entry_committed_r (
          .load(store_entry_committed_set[gi] | store_entry_committed_clr[gi]),
          .din (store_entry_committed_set[gi]),
          .dout(store_entry_committed[gi]),
          .*
      );
    end

  endgenerate


endmodule
